package com.ljw.punishment;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.ljw.punishment.baserule.AndRelationGroup;
import com.ljw.punishment.baserule.CommonRule;
import com.ljw.punishment.baserule.OrRelationGroup;
import com.ljw.punishment.customBiDuifunction.CustomFunction;
import com.ljw.punishment.entry.CheckFireRequest;
import com.ljw.punishment.entry.ShopRuleGroupDAO;
import com.ljw.punishment.entry.ShopRuleInfoDAO;
import com.ljw.punishment.entry.ShopSceneInfoDAO;
import com.ljw.punishment.entry2.RuleFireResponse;
import com.ljw.punishment.rulefactory.CustomRuleFactory;
import com.ljw.punishment.rulesengine.RuleEngineBuilder;
import com.ljw.punishment.service.RuleOperateServiceImpl;
import com.ljw.util.*;
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rule;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.support.composite.CompositeRule;
import org.junit.Test;
import org.mvel2.MVEL;
import org.mvel2.ParserContext;
import org.mvel2.integration.impl.DefaultLocalVariableResolverFactory;

import java.io.Serializable;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author lijunwei
 * @date 2025/5/21
 */
public class RuleMatchDemo {


    @Test
    public void commonRuleEvaluate() {
        Facts facts = getFacts();
        Map<Integer, Facts> resultMapEmptyCollection = new HashMap<>();
        CommonRule commonRule = new CommonRule("场景：" + "关键规则" + System.currentTimeMillis(), "场景总规则", 0, resultMapEmptyCollection);
        //生效时间类型 0 永不过期  固定时间
        if (RuleEffectType.TIME_RANGE.code().equals(Integer.valueOf(1))) {
            Map<String, Object> extendParam = new HashMap<>();
            extendParam.put("startDate", new Date());
            extendParam.put("endDate", new Date());
            commonRule.when("dateAfter(getCurrentDate(),startDate) && dateAfter(endDate,getCurrentDate())", extendParam);
        } else {
            commonRule.when("true");
        }
        commonRule.setRewardCode("check20250519160417339");
        commonRule.setId(2164);
        commonRule.then(String.valueOf(2164));
        boolean evaluate = commonRule.evaluate(facts);
        System.out.println(evaluate);
    }

    @Test
    public void evaluate(){

        ParserContext ctx = new ParserContext();
        Method[] declaredMethods = CustomFunction.class.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            ctx.addImport(declaredMethod.getName(), declaredMethod);
        }
        Map<String, Object> extendParam = new HashMap<>();
        extendParam.put("startDate", DateUtils.stringToDate("2025-01-01 00:00:00"));
        extendParam.put("endDate", DateUtils.stringToDate("2025-07-01 00:00:00"));
        //CustomMvelCondition implements Condition , Serializable
        //  CustomMvelCondition customMvelCondition = new CustomMvelCondition("dateAfter(getCurrentDate(),startDate) && dateAfter(endDate,getCurrentDate())", ctx, extendParam);

        Serializable compiledExpression = MVEL.compileExpression("dateAfter(getCurrentDate(),startDate) && dateAfter(endDate,getCurrentDate())", ctx);
        Map<String,Object> extendParams= extendParam;

        DefaultLocalVariableResolverFactory variableResolverFactory = new DefaultLocalVariableResolverFactory(extendParam);
        Map<String, Object> map = new HashMap<>();
        boolean b = (boolean) MVEL.executeExpression(compiledExpression, map, variableResolverFactory);
        System.out.println(b);

    }

    @Test
    public void evaluate2(){

//        ParserContext ctx = new ParserContext();
//        Method[] declaredMethods = CustomFunction.class.getDeclaredMethods();
//        for (Method declaredMethod : declaredMethods) {
//            ctx.addImport(declaredMethod.getName(), declaredMethod);
//        }


        Serializable compiledExpression1 = MVEL.compileExpression("ruleCreateShop==1 &&sameSeasonShopPunishmentNum==1");
        Facts facts1 = getFacts();
        Map<String, Object> extendParams1 = facts1.asMap();
        DefaultLocalVariableResolverFactory variableResolverFactory1 = new DefaultLocalVariableResolverFactory(extendParams1);
        Map<String, Object> map1 = new HashMap<>();
        boolean b1 = (boolean) MVEL.executeExpression(compiledExpression1, map1, variableResolverFactory1);
        System.out.println(b1);

        Serializable compiledExpression2 = MVEL.compileExpression("{\"supervisor\",\"onlineQuality\",\"visitQuality\"} contains operator&&{859,4325000086} contains checkItemId");
        Facts facts2 = getFacts();
        Map<String, Object> extendParams2 = facts2.asMap();
        DefaultLocalVariableResolverFactory variableResolverFactory2 = new DefaultLocalVariableResolverFactory(extendParams2);
        Map<String, Object> map2 = new HashMap<>();
        boolean b2 = (boolean) MVEL.executeExpression(compiledExpression2, map2, variableResolverFactory2);
        System.out.println(b2);

    }

    /**
     * CommonRule extends BasicRule
     *                    BasicRule implements Rule
     *
     * public class AndRelationGroup extends UnitRuleGroup
     *                                       UnitRuleGroup extends CompositeRule
     * 									                         CompositeRule extends BasicRule
     * 															                       BasicRule implements Rule
     * 原始拼装代码的方式执行(未分装方法)
     *
     * @throws Exception
     */
    @Test
    public void originalExcute0() throws Exception {
//        List<ShopSceneInfoDAO> shopSceneInfoDoList = shopSceneInfoRepository.querySceneByCode("check20250519160417339");
//        ShopSceneInfoDAO shopSceneInfoDAO = shopSceneInfoDoList.stream().filter(e -> Integer.valueOf(2164).equals(e.getId())).findFirst().get();

        //shop_scene_info
        String shopSceneInfoDAOStr = "{\"conflictType\":\"throwException\",\"effectiveType\":0,\"executeType\":0," + "\"id\":2164,\"rewardCode\":\"check20250519160417339\",\"sceneName\":\"关键规则\"," + "\"sceneType\":\"check\",\"subviewId\":2301,\"viewId\":811}\n";
        ShopSceneInfoDAO shopSceneInfoDAO = JSONObject.parseObject(shopSceneInfoDAOStr, ShopSceneInfoDAO.class);

        Date now = new Date();
        if (RuleEffectType.TIME_RANGE.code().equals(shopSceneInfoDAO.getEffectiveType()) && (now.before(shopSceneInfoDAO.getBeginDate()) || now.after(shopSceneInfoDAO.getEndDate()))) {
            return;
        }
        String rewardCode = shopSceneInfoDAO.getRewardCode();
        // 基础condition，每个group都要包含的，比如：规则有效时间
        Set<CommonRule> baseRuleSet = new HashSet<>();
        CommonRule commonRule = new CommonRule("场景：" + shopSceneInfoDAO.getSceneName() + RewardCodeGenerator.generatorUuid(), "场景总规则", 0, new HashMap<>());
        if (RuleEffectType.TIME_RANGE.code().equals(shopSceneInfoDAO.getEffectiveType())) {
            Map<String, Object> extendParam = new HashMap<>();
            extendParam.put("startDate", shopSceneInfoDAO.getBeginDate());
            extendParam.put("endDate", shopSceneInfoDAO.getEndDate());
            commonRule.when("dateAfter(getCurrentDate(),startDate) && dateAfter(endDate,getCurrentDate())", extendParam);
        } else {
            commonRule.when("true");
        }
        commonRule.setRewardCode(shopSceneInfoDAO.getRewardCode());
        commonRule.setId(shopSceneInfoDAO.getId());
        commonRule.then(String.valueOf(shopSceneInfoDAO.getId()));
        baseRuleSet.add(commonRule);

        //在 Easy Rules 中，CompositeRule 是一种组合规则，它可以包含多个子规则（Rule）----------------------
        CompositeRule totalGroup = new AndRelationGroup(shopSceneInfoDAO.getId(), shopSceneInfoDAO.getSceneName() + RewardCodeGenerator.generatorUuid(), rewardCode, baseRuleSet);
//        List<ShopRuleGroupDAO> shopRuleGroupDoList = shopRuleGroupRepository.queryShopRuleGroupByCode("check20250519160417339", shopSceneInfoDAO.getId());
//        System.out.println(JSONObject.toJSONString(shopRuleGroupDoList));
        String shopRuleGroupDoListStr = "[{\"description\":\"场景：关键规则, rewardCode :check20250519160417339下的分组\"," + "\"groupPriority\":2147483647,\"groupType\":\"and\",\"id\":2157,"
                + "\"name\":\"场景：关键规则规则组4b30f3e6780b4623a068f1505eef792f\",\"rewardCode\":\"check20250519160417339\"," + "\"sceneId\":2164}]\n";
        List<ShopRuleGroupDAO> shopRuleGroupDoList = JSON.parseObject(shopRuleGroupDoListStr, new TypeReference<List<ShopRuleGroupDAO>>() {});

        //shop_rule_group
        for (ShopRuleGroupDAO shopRuleGroupDAO : shopRuleGroupDoList) {
            String groupType = shopRuleGroupDAO.getGroupType();
            //and  或者 or
            RuleRelationTypeEnum relationTypeEnum = RuleRelationTypeEnum.getRelationVyCode(groupType);
            if (relationTypeEnum == null) {
                throw new Exception(rewardCode + "不存在的规则组类型");
            }
            // 模块下的规则  处理 每个 shop_rule_group 下对应的多个 shop_rule_info
            // Set<CommonRule> ruleGroup = ruleLoader.createRuleByGroupId(rewardCode, shopRuleGroupDAO.getId());
            //shop_rule_info
//            List<ShopRuleInfoDAO> shopRuleInfoDoS = shopRuleInfoRepository.queryShopRuleByCode(rewardCode,shopRuleGroupDAO.getId());
//            System.out.println(JSON.toJSONString(shopRuleInfoDoS));

            String shopRuleInfoDoSstr1="[{\n" +
                    "\t\"description\": \"拓展规则描述: 规则编码check20250519160417339规则场景名称： 关键规则规则内容拓展规则描述：操作人包含{\\\"supervisor\\\",\\\"onlineQuality\\\",\\\"visitQuality\\\"} 检查项包含{859,4325000086}\",\n" +
                    "\t\"id\": 4244,\n" +
                    "\t\"name\": \"关键规则规则扩展模块9bab31c0d97244eab00f6bc1ac89dfb7\",\n" +
                    "\t\"rewardCode\": \"check20250519160417339\",\n" +
                    "\t\"ruleContext\": \"[{\\\"condition\\\":\\\"{\\\\\\\"supervisor\\\\\\\",\\\\\\\"onlineQuality\\\\\\\",\\\\\\\"visitQuality\\\\\\\"} contains operator&&{859,4325000086} contains checkItemId\\\",\\\"actions\\\":[\\\"\\\"],\\\"description\\\":\\\"拓展规则描述：操作人包含{\\\\\\\"supervisor\\\\\\\",\\\\\\\"onlineQuality\\\\\\\",\\\\\\\"visitQuality\\\\\\\"} 检查项包含{859,4325000086}\\\"}]\",\n" +
                    "\t\"ruleGroupId\": 2157,\n" +
                    "\t\"rulePriority\": 2147483647\n" +
                    "}]";

            String shopRuleInfoDoSstr = "[{\"description\":\"规则描述: 规则编码check20250519160417339规则场景名称： " +
                    "关键规则规则内容规则描述：检查创建时是否是新店等于1 门店同季度触犯次数等于1\",\"id\":4240," +
                    "\"name\":\"关键规则规则模块b1b1907d0ac34b9dabc9a82fb8206b43\",\"rewardCode\":\"check20250519160417339\"," +
                    "\"ruleContext\":\"[{\\\"condition\\\":\\\"ruleCreateShop==1 &&sameSeasonShopPunishmentNum==1 " +
                    "\\\",\\\"actions\\\":[\\\"\\\"],\\\"description\\\":\\\"规则描述：检查创建时是否是新店等于1 门店同季度触犯次数等于1\\\"}]\"," +
                    "\"ruleGroupId\":2157,\"rulePriority\":2147483647},{\"description\":\"拓展规则描述: " +
                    "规则编码check20250519160417339规则场景名称： 关键规则规则内容拓展规则描述：操作人包含{\\\"supervisor\\\",\\\"onlineQuality\\\"," +
                    "\\\"visitQuality\\\"} 检查项包含{859,4325000086}\",\"id\":4244," +
                    "\"name\":\"关键规则规则扩展模块9bab31c0d97244eab00f6bc1ac89dfb7\"," +
                    "\"rewardCode\":\"check20250519160417339\"," +
                    "\"ruleContext\":\"[{\\\"condition\\\":\\\"{\\\\\\\"supervisor\\\\\\\"," +
                    "\\\\\\\"onlineQuality\\\\\\\",\\\\\\\"visitQuality\\\\\\\"} contains operator&&{859,4325000086} " +
                    "contains checkItemId\\\",\\\"actions\\\":[\\\"\\\"]," +
                    "\\\"description\\\":\\\"拓展规则描述：操作人包含{\\\\\\\"supervisor\\\\\\\",\\\\\\\"onlineQuality\\\\\\\"," +
                    "\\\\\\\"visitQuality\\\\\\\"} 检查项包含{859,4325000086}\\\"}]\",\"ruleGroupId\":2157," +
                    "\"rulePriority\":2147483647}]\n";
            List<ShopRuleInfoDAO> shopRuleInfoDoS = JSON.parseObject(shopRuleInfoDoSstr, new TypeReference<List<ShopRuleInfoDAO>>() {});

            CustomRuleFactory customRuleFactory = new CustomRuleFactory(CustomRuleFactory.createJSONReader(), CustomRuleFactory.initParseContext());
            Set<CommonRule> ruleGroup = new HashSet<>();
            for (ShopRuleInfoDAO shopRuleInfoDAO : shopRuleInfoDoS) {
                String ruleContext = shopRuleInfoDAO.getRuleContext();
                CommonRule rule = (CommonRule) customRuleFactory.createRule(new StringReader(ruleContext));
                rule.setPriority(shopRuleInfoDAO.getRulePriority());
                rule.setId(shopRuleInfoDAO.getId());
                rule.setName(shopRuleInfoDAO.getName());
                rule.setRewardCode(shopRuleInfoDAO.getRewardCode());
                rule.setDescription("CommonRule : {  id = " + shopRuleInfoDAO.getId() + ", name = " + shopRuleInfoDAO.getName() + ",rewardCode = " + shopRuleInfoDAO.getRewardCode() + "}");
                ruleGroup.add(rule);
            }

            CompositeRule compositeRule = null;
            if (RuleRelationTypeEnum.AND == relationTypeEnum) {
                compositeRule = new AndRelationGroup(shopRuleGroupDAO.getId(), shopRuleGroupDAO.getName(), rewardCode, ruleGroup);
            }
            if (RuleRelationTypeEnum.OR == relationTypeEnum) {
                compositeRule = new OrRelationGroup(shopRuleGroupDAO.getId(), shopRuleGroupDAO.getName(), rewardCode, ruleGroup);
            }
            totalGroup.addRule(compositeRule);
        }

        List<CompositeRule> ruleList = new ArrayList<>();
        ruleList.add(totalGroup);

        Facts facts = getFacts();
        //判断是否满足执行条件
        //evaluate 方法用于评估规则的条件部分（即 if 部分）。它检查规则是否符合预定的条件，并返回一个布尔值（通常是 true 或 false）。如果条件为 true，表示规则可以继续执行。如果条件为 false，规则不再继续执行。
        //evaluate 方法通常在规则引擎判断条件时使用。它用于在规则引擎内部检查是否满足规则的触发条件。
        //在条件评估时，用来检查规则条件是否成立。作为规则的一部分，用于在决策树或规则链中判断是否执行某个动作。
        boolean matchStatus = totalGroup.evaluate(facts);
        //evaluate方法通过后调用此方法
        //execute 方法用于执行规则的动作部分（即 then 部分）。它是在规则条件满足时被调用的，执行相应的操作，比如修改事实、调用外部系统接口、更新数据库等
        //execute 方法通常在规则条件评估为 true 后被调用，用于执行规则的动作部分。
        //在规则条件为 true 时，执行某些动作或任务。 修改数据、触发事件、进行计算等。
        if (matchStatus) {
            totalGroup.execute(facts);
        }
        System.out.println(ruleList);

        Rules rules = new Rules();
        for (CompositeRule compositeRule : ruleList) {
            rules.register(compositeRule);
        }

        Iterator<Rule> iterator = rules.iterator();
        Set<Rule> ruleSet = new HashSet<>();
        while (iterator.hasNext()) {
            ruleSet.add(iterator.next());
        }
        System.out.println(ruleSet);

        RuleOperateServiceImpl ruleOperateService = new RuleOperateServiceImpl();
        Map<Integer, Facts> resultFromRules = ruleOperateService.getResultFromRules(ruleSet);
        System.out.println("结果:" + resultFromRules);
        Set<Integer> keyedSet = resultFromRules.keySet();
        List<RuleFireResponse> ruleFireResponses = ruleOperateService.getRuleFireResponses(keyedSet);
        System.out.println(JSON.toJSONString(ruleFireResponses));

    }

    private static Facts getFacts() {
        String str = "{\"operator\":\"supervisor\",\"checkItemId\":859,\"ruleCreateShop\":1," +
                "\"sameSeasonShopPunishmentNum\":1,\"sameSeasonTechPunishmentNum\":1,\"actorDate\":1747641964000," +
                "\"techType\":-1,\"techId\":null,\"newTech\":-1}";
        CheckFireRequest ruleFireRequest = JSONObject.parseObject(str, CheckFireRequest.class);

        Facts facts = new Facts();
        facts.put(LibraryCodeEnum.ACTOR_DATE.getCode(), RuleDateUtil.timeToStringByFormat(ruleFireRequest.getActorDate(), RuleDateUtil.dateTimeString));
        facts.put(LibraryCodeEnum.OPERATOR.getCode(), ruleFireRequest.getOperator());
        facts.put(LibraryCodeEnum.CHECK_ITEM_ID.getCode(), ruleFireRequest.getCheckItemId());
        facts.put(LibraryCodeEnum.RULE_CREATE_SHOP.getCode(), ruleFireRequest.getRuleCreateShop());
        facts.put(LibraryCodeEnum.SAME_SEASON_SHOP_PUNISHMENT_NUM.getCode(), ruleFireRequest.getSameSeasonShopPunishmentNum());
        facts.put(LibraryCodeEnum.SAME_SEASON_TECH_PUNISHMENT_NUM.getCode(), ruleFireRequest.getSameSeasonTechPunishmentNum());

        // 新增技师类型 技师标签
        facts.put(LibraryCodeEnum.BE_NEW_TECH.getCode(), ruleFireRequest.getNewTech());
        if (ruleFireRequest.getTechType() != null) {facts.put(LibraryCodeEnum.PUNISH_TECH_TYPE.getCode(), ruleFireRequest.getTechType());
        } else {facts.put(LibraryCodeEnum.PUNISH_TECH_TYPE.getCode(), CommonConstant.DEFAULT_TECH_TYPE);
        }
        return facts;
    }

    /**
     * 获取规则引擎
     */
    @Test
    public void getRuleEngine() {
        RuleEngineBuilder ruleEngineBuilder = new RuleEngineBuilder();
        RulesEngine rulesEngine = ruleEngineBuilder.buildRuleEngine("check");
        System.out.println(rulesEngine);
    }


    /**
     * CommonRule extends BasicRule
     *                    BasicRule implements Rule
     *
     * public class AndRelationGroup extends UnitRuleGroup
     *                                       UnitRuleGroup extends CompositeRule
     * 									                         CompositeRule extends BasicRule
     * 															                       BasicRule implements Rule
     * 执行规则引擎
     */
    @Test
    public void doFire() throws Exception {
        Facts facts = getFacts();
        RuleOperateServiceImpl ruleOperateService = new RuleOperateServiceImpl();

        List<RuleFireResponse> response = ruleOperateService.doFire(facts, SceneTypeEnum.CHECK_PUNISHMENT.getSceneCode());

        System.out.println(JSONObject.toJSONString(response));
    }

}
